package com.snap.update.update;


import com.snap.update.AppConfig;

public class UpdateCmd {

    private static byte[] generate(int length, int address, int funCode) {
        byte[] cmd = new byte[length];
        cmd[0] = AppConfig.Dynamic.CMD_START1;
        cmd[1] = AppConfig.Dynamic.CMD_START2;
        cmd[2] = (byte) address;
        cmd[3] = (byte) ((length - 2 & 0xff00) >> 8);
        cmd[4] = (byte) (length - 2 & 0xff);
        cmd[5] = (byte) funCode;
        return cmd;
    }

    private static void addParity(byte[] cmd) {
        int crc = UpdateCmd.crc(cmd, 0, cmd.length - 2);
        cmd[cmd.length - 2] = (byte) ((crc & 0xff00) >> 8);
        cmd[cmd.length - 1] = (byte) (crc & 0xff);
    }

    public static byte[] init(int address) {
        byte[] cmd = generate(8, address, 0x60);
        addParity(cmd);
        return cmd;
    }

    public static byte[] getStatus(int address) {
        byte[] cmd = generate(8, address, 0x61);
        addParity(cmd);
        return cmd;
    }

    public static byte[] getHardInfo(int address) {
        byte[] cmd = generate(8, address, 0x62);
        addParity(cmd);
        return cmd;
    }

    public static byte[] config(int address, int fileSize, int fragmentSize, int major, int minor, int patch) {
        byte[] cmd = generate(16, address, 0x63);

        cmd[6] = (byte) ((fileSize & 0xff0000) >> 16);
        cmd[7] = (byte) ((fileSize & 0xff00) >> 8);
        cmd[8] = (byte) (fileSize & 0xff);

        cmd[9] = (byte) ((fragmentSize & 0xff00) >> 8);
        cmd[10] = (byte) (fragmentSize & 0xff);

        cmd[11] = (byte) major;
        cmd[12] = (byte) minor;
        cmd[13] = (byte) patch;

        addParity(cmd);
        return cmd;
    }

    public static byte[] data(int address, int index, byte[] data, int begin, int length) {
        byte[] cmd = generate(8 + 1 + length, address, 0x64);
        cmd[6] = (byte) index;
        System.arraycopy(data, begin, cmd, 7, length);
        addParity(cmd);
        return cmd;
    }

    public static byte[] jump(int address) {
        byte[] cmd = generate(8, address, 0x65);
        addParity(cmd);
        return cmd;
    }

    /**
     * 验证这串字节数组是否符合协议
     */
    static boolean cmdValidate(byte[] cmd) {
        if (cmd == null) return false;
        if (cmd.length < 8) return false;
        if (cmd[0] != AppConfig.Dynamic.CMD_START1) return false;
        if (cmd[1] != AppConfig.Dynamic.CMD_START2) return false;
        int frameLength = ((cmd[3] & 0xff) << 8) + (cmd[4] & 0xff);
        if (frameLength != cmd.length - 2) return false;
        int crc = UpdateCmd.crc(cmd, 0, cmd.length - 2);
        return cmd[cmd.length - 2] == (byte) ((crc & 0xff00) >> 8)
                && cmd[cmd.length - 1] == (byte) (crc & 0xff);
    }

    /**
     * 是否包含大于1条指令
     */
    static boolean isOneMore(byte[] cmd) {
        return cmd != null
                && cmd.length > 8
                && cmd[0] == AppConfig.Dynamic.CMD_START1
                && cmd[1] == AppConfig.Dynamic.CMD_START2
                && cmd.length > ((cmd[3] & 0xff) << 8) + (cmd[4] & 0xff) + 2;
    }

    /**
     * 验证发送的指令和收到的指令 设备号 地址号 模块号 功能号等是否一致
     * 两条之间进行比较
     */
    static boolean cmdMatch(byte[] cmdA, byte[] cmdB) {
        if (cmdA[2] != cmdB[2]) return false;
        if (cmdA[5] != cmdB[5]) return false;
        return true;
    }

    /******************************************************************************
     * Name:    CRC-16/MODBUS       x16+x15+x2+1
     * Poly:    0x8005
     * Init:    0xFFFF
     * Refin:   True
     * Refout:  True
     * Xorout:  0x0000
     * Note:
     *****************************************************************************/
    public static int crc(byte[] bytes, int offset, int length) {
        int CRC = 0x0000ffff;
        int POLYNOMIAL = 0x0000a001;
        length += offset;
        int i, j;
        for (i = offset; i < length; i++) {
            CRC ^= ((int) bytes[i] & 0x000000ff);
            for (j = 0; j < 8; j++) {
                if ((CRC & 0x00000001) != 0) {
                    CRC >>= 1;
                    CRC ^= POLYNOMIAL;
                } else {
                    CRC >>= 1;
                }
            }
        }
        return CRC;
    }
}
